#!/usr/bin/with-contenv bashio
# vim: ft=bash
# shellcheck shell=bash
# ==============================================================================
# Start sshd service if enabled
# ==============================================================================
CERT_DIR=/data/letsencrypt
WORK_DIR=/data/workdir
ACME_ARGUMENTS=()
KEY_ARGUMENTS=()
EAB_ARGUMENTS=()
ADDITIONAL_ARGS=()

EMAIL=$(bashio::config 'email')
DOMAINS=$(bashio::config 'domains')
KEYFILE=$(bashio::config 'keyfile')
CERTFILE=$(bashio::config 'certfile')
CHALLENGE=$(bashio::config 'challenge')
DNS_PROVIDER=$(bashio::config 'dns.provider')
ACME_SERVER=$(bashio::config 'acme_server')
ACME_ROOT_CA_CERT=$(bashio::config 'acme_root_ca_cert')
EAB_KID=$(bashio::config 'eab_kid')
EAB_HMAC_KEY=$(bashio::config 'eab_hmac_key')
DRY_RUN=$(bashio::config 'dry_run')
TEST_CERT=$(bashio::config 'test_cert')
VERBOSE=$(bashio::config 'verbose')

if [ "${CHALLENGE}" == "dns" ]; then
    bashio::log.info "Selected DNS Provider: ${DNS_PROVIDER}"

    PROPAGATION_SECONDS=60
    if bashio::config.exists 'dns.propagation_seconds'; then
        PROPAGATION_SECONDS="$(bashio::config 'dns.propagation_seconds')"
    fi
    bashio::log.info "Use propagation seconds: ${PROPAGATION_SECONDS}"

    case "${DNS_PROVIDER}" in
        # Azure
        'dns-azure')
            bashio::config.require 'dns.azure_config'
            AZURE_CREDS="$(bashio::config 'dns.azure_config')"

            export AZURE_CREDS
            if [ -f "/share/${AZURE_CREDS}" ]; then
              cp -f "/share/${AZURE_CREDS}" "/data/azure_creds"
              chmod 600 "/data/azure_creds"
            else
              bashio::log.error "Azure credentials file '${AZURE_CREDS}' not found in /share directory"
              exit 1
            fi
            ACME_ARGUMENTS+=("--authenticator" "${DNS_PROVIDER}" "--${DNS_PROVIDER}-config" "/data/azure_creds")
            ;;

        # CloudFlare
        'dns-cloudflare')
            if bashio::config.exists 'dns.cloudflare_api_token'; then
              bashio::log.info "Use CloudFlare token"
              echo "dns_cloudflare_api_token = $(bashio::config 'dns.cloudflare_api_token')" >> "/data/dnsapikey"
            else
              bashio::log.warning "Use CloudFlare global key (not recommended!)"
              echo -e "dns_cloudflare_email = $(bashio::config 'dns.cloudflare_email')\n" \
                  "dns_cloudflare_api_key = $(bashio::config 'dns.cloudflare_api_key')\n" >> "/data/dnsapikey"
            fi
            ACME_ARGUMENTS+=("--${DNS_PROVIDER}" "--${DNS_PROVIDER}-credentials" "/data/dnsapikey" "--${DNS_PROVIDER}-propagation-seconds" "${PROPAGATION_SECONDS}")
            ;;

        # ClouDNS
        'dns-cloudns')
            bashio::config.require 'dns.cloudns_auth_password'
            ACME_ARGUMENTS+=("--authenticator" "${DNS_PROVIDER}" "--${DNS_PROVIDER}-credentials" "/data/dnsapikey" "--${DNS_PROVIDER}-propagation-seconds" "${PROPAGATION_SECONDS}")
            ;;

        # desec
        'dns-desec')
            bashio::config.require 'dns.desec_token'
            ACME_ARGUMENTS+=("--authenticator" "${DNS_PROVIDER}" "--${DNS_PROVIDER}-credentials" "/data/dnsapikey" "--${DNS_PROVIDER}-propagation-seconds" "${PROPAGATION_SECONDS}")
            ;;

        # DigitalOcean
        'dns-digitalocean')
            bashio::config.require 'dns.digitalocean_token'
            ACME_ARGUMENTS+=("--authenticator" "${DNS_PROVIDER}" "--${DNS_PROVIDER}-credentials" "/data/dnsapikey" "--${DNS_PROVIDER}-propagation-seconds" "${PROPAGATION_SECONDS}")
            ;;

        # DirectAdmin
        'dns-directadmin')
            bashio::config.require 'dns.directadmin_url'
            bashio::config.require 'dns.directadmin_username'
            bashio::config.require 'dns.directadmin_password'
            ACME_ARGUMENTS+=("--authenticator" "${DNS_PROVIDER}" "--${DNS_PROVIDER}-credentials" "/data/dnsapikey" "--${DNS_PROVIDER}-propagation-seconds" "${PROPAGATION_SECONDS}")
            ;;

        # domainoffensive
        'dns-domainoffensive')
            bashio::config.require 'dns.domainoffensive_token'
            ACME_ARGUMENTS+=("--authenticator" "${DNS_PROVIDER}" "--${DNS_PROVIDER}-credentials" "/data/dnsapikey")
            ;;

        # Dreamhost
        'dns-dreamhost')
            bashio::config.require 'dns.dreamhost_baseurl'
            bashio::config.require 'dns.dreamhost_api_key'
            ACME_ARGUMENTS+=("--authenticator" "${DNS_PROVIDER}" "--${DNS_PROVIDER}-credentials" "/data/dnsapikey")
            ;;

        # DuckDNS
        'dns-duckdns')
            bashio::config.require 'dns.duckdns_token'
            ACME_ARGUMENTS+=("--authenticator" "${DNS_PROVIDER}" "--${DNS_PROVIDER}-credentials" "/data/dnsapikey" "--${DNS_PROVIDER}-propagation-seconds" "${PROPAGATION_SECONDS}")
            ;;

        # Dynu
        'dns-dynu')
            bashio::config.require 'dns.dynu_auth_token'
            ACME_ARGUMENTS+=("--authenticator" "${DNS_PROVIDER}" "--${DNS_PROVIDER}-credentials" "/data/dnsapikey" "--${DNS_PROVIDER}-propagation-seconds" "${PROPAGATION_SECONDS}")
            ;;

        # easyDNS
        'dns-easydns')
            bashio::config.require 'dns.easydns_key'
            bashio::config.require 'dns.easydns_token'
            bashio::config.require 'dns.easydns_endpoint'
            ACME_ARGUMENTS+=("--authenticator" "${DNS_PROVIDER}" "--${DNS_PROVIDER}-credentials" "/data/dnsapikey" "--${DNS_PROVIDER}-propagation-seconds" "${PROPAGATION_SECONDS}")
            ;;

        # Eurodns
        'dns-eurodns')
            bashio::config.require 'dns.eurodns_applicationId'
            bashio::config.require 'dns.eurodns_apiKey'
            ACME_ARGUMENTS+=("--authenticator" "${DNS_PROVIDER}" "--${DNS_PROVIDER}-credentials" "/data/dnsapikey" "--${DNS_PROVIDER}-propagation-seconds" "${PROPAGATION_SECONDS}")
            ;;

        # Gandi
        'dns-gandi')
            if bashio::config.exists 'dns.gandi_sharing_id'; then
                bashio::log.info "Use Gandi sharing ID"
                echo "dns_gandi_sharing_id = $(bashio::config 'dns.gandi_sharing_id')" >> "/data/dnsapikey"
            fi
            if bashio::config.exists 'dns.gandi_token'; then
                bashio::log.info "Use Gandi gandi_token"
                echo "dns_gandi_token = $(bashio::config 'dns.gandi_token')" >> "/data/dnsapikey"
            fi
            if bashio::config.exists 'dns.gandi_api_key'; then
                bashio::log.info "Use Gandi gandi_api_key"
                echo "dns_gandi_api_key = $(bashio::config 'dns.gandi_api_key')" >> "/data/dnsapikey"
            fi
            ACME_ARGUMENTS+=("--authenticator" "${DNS_PROVIDER}" "--${DNS_PROVIDER}-credentials" "/data/dnsapikey" "--${DNS_PROVIDER}-propagation-seconds" "${PROPAGATION_SECONDS}" )
            ;;

        # GoDaddy
        'dns-godaddy')
            bashio::config.require 'dns.godaddy_secret'
            bashio::config.require 'dns.godaddy_key'
            ACME_ARGUMENTS+=("--authenticator" "${DNS_PROVIDER}" "--${DNS_PROVIDER}-credentials" "/data/dnsapikey" "--${DNS_PROVIDER}-propagation-seconds" "${PROPAGATION_SECONDS}")
            ;;

        # Google
        'dns-google')
            bashio::config.require 'dns.google_creds'
            GOOGLE_CREDS="$(bashio::config 'dns.google_creds')"
            export GOOGLE_CREDS
            if [ -f "/share/${GOOGLE_CREDS}" ]; then
                cp -f "/share/${GOOGLE_CREDS}" "/data/${GOOGLE_CREDS}"
                chmod 600 "/data/${GOOGLE_CREDS}"
            else
                bashio::log.error "Google credentials file '${GOOGLE_CREDS}' not found in /share directory"
                exit 1
            fi
            ACME_ARGUMENTS+=("--${DNS_PROVIDER}" "--${DNS_PROVIDER}-credentials" "/data/${GOOGLE_CREDS}")
            ;;

        # Hurricane Electric
        'dns-he')
            bashio::config.require 'dns.he_user'
            bashio::config.require 'dns.he_pass'
            ACME_ARGUMENTS+=("--authenticator" "dns-hurricane_electric" "--dns-hurricane_electric-credentials" "/data/dnsapikey" "--dns-hurricane_electric-propagation-seconds" "${PROPAGATION_SECONDS}")
            ;;

        # Hetzner
        'dns-hetzner')
            bashio::config.require 'dns.hetzner_api_token'
            ACME_ARGUMENTS+=("--authenticator" "${DNS_PROVIDER}" "--${DNS_PROVIDER}-credentials" "/data/dnsapikey" "--${DNS_PROVIDER}-propagation-seconds" "${PROPAGATION_SECONDS}")
            ;;

        # Infomaniak
        'dns-infomaniak')
            bashio::config.require 'dns.infomaniak_api_token'
            ACME_ARGUMENTS+=("--authenticator" "${DNS_PROVIDER}" "--${DNS_PROVIDER}-credentials" "/data/dnsapikey" "--${DNS_PROVIDER}-propagation-seconds" "${PROPAGATION_SECONDS}")
            ;;

        # INWX
        'dns-inwx')
            bashio::config.require 'dns.inwx_username'
            bashio::config.require 'dns.inwx_password'
            bashio::config.require 'dns.inwx_shared_secret'
            ACME_ARGUMENTS+=("--authenticator" "${DNS_PROVIDER}" "--${DNS_PROVIDER}-credentials" "/data/dnsapikey" "--${DNS_PROVIDER}-propagation-seconds" "${PROPAGATION_SECONDS}")
            ;;

        # IONOS
        'dns-ionos')
            bashio::config.require 'dns.ionos_prefix'
            bashio::config.require 'dns.ionos_secret'
            bashio::config.require 'dns.ionos_endpoint'
            ACME_ARGUMENTS+=("--authenticator" "${DNS_PROVIDER}" "--${DNS_PROVIDER}-credentials" "/data/dnsapikey" "--${DNS_PROVIDER}-propagation-seconds" "${PROPAGATION_SECONDS}")
            ;;

        # Joker
        'dns-joker')
            bashio::config.require 'dns.joker_username'
            bashio::config.require 'dns.joker_password'
            ACME_ARGUMENTS+=("--authenticator" "${DNS_PROVIDER}" "--${DNS_PROVIDER}-credentials" "/data/dnsapikey" "--${DNS_PROVIDER}-propagation-seconds" "${PROPAGATION_SECONDS}")
            ;;

        # Loopia
        'dns-loopia')
            bashio::config.require 'dns.loopia_user'
            bashio::config.require 'dns.loopia_password'
            if (( PROPAGATION_SECONDS < 900 )); then
                bashio::log.info "Increasing DNS propagation limit for Loopia to at least 900 seconds due to caching issues."
                PROPAGATION_SECONDS=900
            fi
            ACME_ARGUMENTS+=("--authenticator" "${DNS_PROVIDER}" "--${DNS_PROVIDER}-credentials" "/data/dnsapikey" "--${DNS_PROVIDER}-propagation-seconds" "${PROPAGATION_SECONDS}")
            ;;

        # mijn.host
        'dns-mijn-host')
            bashio::config.require 'dns.mijn_host_api_key'
            ACME_ARGUMENTS+=("--authenticator" "${DNS_PROVIDER}" "--${DNS_PROVIDER}-credentials" "/data/dnsapikey" "--${DNS_PROVIDER}-propagation-seconds" "${PROPAGATION_SECONDS}")
            ;;

        # Namecheap
        'dns-namecheap')
            bashio::config.require 'dns.namecheap_username'
            bashio::config.require 'dns.namecheap_api_key'
            ACME_ARGUMENTS+=("--authenticator" "${DNS_PROVIDER}" "--${DNS_PROVIDER}-credentials" "/data/dnsapikey" "--${DNS_PROVIDER}-propagation-seconds" "${PROPAGATION_SECONDS}")
            ;;

        # Netcup
        'dns-netcup')
            bashio::config.require 'dns.netcup_customer_id'
            bashio::config.require 'dns.netcup_api_key'
            bashio::config.require 'dns.netcup_api_password'
            ACME_ARGUMENTS+=("--authenticator" "${DNS_PROVIDER}" "--${DNS_PROVIDER}-credentials" "/data/dnsapikey" "--${DNS_PROVIDER}-propagation-seconds" "${PROPAGATION_SECONDS}")
            ;;

        # Njalla
        'dns-njalla')
            bashio::config.require 'dns.njalla_token'
            ACME_ARGUMENTS+=("--authenticator" "${DNS_PROVIDER}" "--${DNS_PROVIDER}-credentials" "/data/dnsapikey" "--${DNS_PROVIDER}-propagation-seconds" "${PROPAGATION_SECONDS}")
            ;;

        # noris network
        'dns-noris')
            bashio::config.require 'dns.noris_token'
            ACME_ARGUMENTS+=("--authenticator" "${DNS_PROVIDER}" "--${DNS_PROVIDER}-credentials" "/data/dnsapikey" "--${DNS_PROVIDER}-propagation-seconds" "${PROPAGATION_SECONDS}")
            ;;

        # Plesk
        'dns-plesk')
            bashio::config.require 'dns.plesk_username'
            bashio::config.require 'dns.plesk_password'
            bashio::config.require 'dns.plesk_api_url'
            if (( PROPAGATION_SECONDS < 120 )); then
                bashio::log.info "Increasing DNS propagation limit for Plesk to at least 120 seconds."
                PROPAGATION_SECONDS=120
            fi
            ACME_ARGUMENTS+=("--authenticator" "${DNS_PROVIDER}" "--${DNS_PROVIDER}-credentials" "/data/dnsapikey" "--${DNS_PROVIDER}-propagation-seconds" "${PROPAGATION_SECONDS}")
            ;;

        #Porkbun
        'dns-porkbun')
            bashio::config.require 'dns.porkbun_key'
            bashio::config.require 'dns.porkbun_secret'
            ACME_ARGUMENTS+=("--authenticator" "${DNS_PROVIDER}" "--${DNS_PROVIDER}-credentials" "/data/dnsapikey" "--${DNS_PROVIDER}-propagation-seconds" "${PROPAGATION_SECONDS}")
            ;;

        # rfc2136
        'dns-rfc2136')
            ACME_ARGUMENTS+=("--${DNS_PROVIDER}" "--${DNS_PROVIDER}-credentials" "/data/dnsapikey" "--${DNS_PROVIDER}-propagation-seconds" "${PROPAGATION_SECONDS}")
            ;;

        # route53 - AWS
        'dns-route53')
            bashio::config.require 'dns.aws_access_key_id'
            bashio::config.require 'dns.aws_secret_access_key'
            AWS_ACCESS_KEY_ID="$(bashio::config 'dns.aws_access_key_id')"
            AWS_SECRET_ACCESS_KEY="$(bashio::config 'dns.aws_secret_access_key')"
            export AWS_ACCESS_KEY_ID
            export AWS_SECRET_ACCESS_KEY
            ACME_ARGUMENTS+=("--${DNS_PROVIDER}")
            ;;

        # Simply
        'dns-simply')
            bashio::config.require 'dns.simply_account_name'
            bashio::config.require 'dns.simply_api_key'
            ACME_ARGUMENTS+=("--authenticator" "${DNS_PROVIDER}" "--${DNS_PROVIDER}-credentials" "/data/dnsapikey" "--${DNS_PROVIDER}-propagation-seconds" "${PROPAGATION_SECONDS}")
            ;;

        # TransIP
        'dns-transip')
            bashio::config.require 'dns.transip_username'
            bashio::config.require 'dns.transip_api_key'
            if (( PROPAGATION_SECONDS < 240 )); then
                bashio::log.info "Increasing DNS propagation time for TransIP to at least 240 seconds."
                PROPAGATION_SECONDS=240
            fi
            ACME_ARGUMENTS+=("--authenticator" "${DNS_PROVIDER}" "--${DNS_PROVIDER}-credentials" "/data/dnsapikey" "--${DNS_PROVIDER}-propagation-seconds" "${PROPAGATION_SECONDS}")
              ;;

        # WebSupport
        'dns-websupport')
            bashio::config.require 'dns.websupport_identifier'
            bashio::config.require 'dns.websupport_secret_key'
            ACME_ARGUMENTS+=("--authenticator" "${DNS_PROVIDER}" "--${DNS_PROVIDER}-credentials" "/data/dnsapikey" "--${DNS_PROVIDER}-propagation-seconds" "${PROPAGATION_SECONDS}")
            ;;

        #All other DNS providers
        *)
            ACME_ARGUMENTS+=("--${DNS_PROVIDER}" "--${DNS_PROVIDER}-credentials" "/data/dnsapikey")
            ;;
    esac
else
    bashio::log.info "Selected HTTP verification"
    ACME_ARGUMENTS+=("--standalone")
fi

if bashio::config.has_value 'acme_server'; then
    ACME_ARGUMENTS+=("--server" "${ACME_SERVER}")
    if bashio::config.has_value 'acme_root_ca_cert'; then
        bashio::log.info "Updating the trust store by adding the provided custom root certificate"
        # Address potential header and footer errors from YAML mutiline configuration.
        # Check the certificate and import it in the local trust store.
        # Certbot relies on the Requests Python library, which uses a built-in
        # trust store. This is replaced by the updated local trust store.
        echo "${ACME_ROOT_CA_CERT}" \
          | sed 's/----- /-----\n/g' | sed 's/ -----/\n-----/g' \
          | openssl x509  > /usr/local/share/ca-certificates/acme_root_ca.crt \
          && update-ca-certificates --fresh \
          && export REQUESTS_CA_BUNDLE=/etc/ssl/cert.pem
    fi
else
    # Relevant only for default Let's Encrypt servers.
    ACME_ARGUMENTS+=(--preferred-chain 'ISRG Root X1')
    if [ "${DRY_RUN}" = "true" ]; then
        ADDITIONAL_ARGS+=("--dry-run")
    fi
    if [ "${TEST_CERT}" = "true" ]; then
        ADDITIONAL_ARGS+=("--test-cert")
    fi
fi

if [ "${VERBOSE}" = "true" ]; then
    ADDITIONAL_ARGS+=("-vvv")
fi

# Gather all domains into a plaintext file
DOMAIN_ARR=()
for line in $DOMAINS; do
    DOMAIN_ARR+=(-d "$line")
done
echo "$DOMAINS" > /data/domains.gen

# Key detection or manual ECDSA/RSA selection
if bashio::config.exists 'key_type'; then
    # Use key type set in configuration
    KEY_TYPE=$(bashio::config 'key_type')
    KEY_ARGUMENTS+=("--key-type" "${KEY_TYPE}")
    if [ "${KEY_TYPE}" == "ecdsa" ]; then
        if bashio::config.exists 'elliptic_curve'; then
            ELLIPTIC_CURVE=$(bashio::config 'elliptic_curve')
            KEY_ARGUMENTS+=("--elliptic-curve" "${ELLIPTIC_CURVE}")
        else
            KEY_ARGUMENTS+=("--elliptic-curve" "secp384r1")
        fi
    fi
else
    bashio::log.info "Detecting existing certificate type for ${DOMAIN_ARR[1]}"
    readarray -t CBCERTS < <(certbot certificates --non-interactive --cert-name "${DOMAIN_ARR[1]}" --config-dir "$CERT_DIR" --work-dir "$WORK_DIR")
    for output in "${CBCERTS[@]}"; do
        # shellcheck disable=SC2076
        if [[ $output =~ "No certificates found." ]]; then
            bashio::log.info "No certificate found - using 'ecdsa' key type."
            KEY_ARGUMENTS+=("--key-type" "ecdsa")
            break
        fi
        if [[ $output =~ "Key Type: RSA" ]]; then
            bashio::log.info "Existing certificate using 'rsa' key type."
            KEY_ARGUMENTS+=("--key-type" "rsa")
            break
        fi
        if [[ $output =~ "Key Type: ECDSA" ]]; then
            bashio::log.info "Existing certificate using 'ecdsa' key type."
            KEY_ARGUMENTS+=("--key-type" "ecdsa")
            break
        fi
    done
fi

# Set External Account Binding
if bashio::config.has_value 'eab_kid' ; then
    bashio::config.require 'eab_hmac_key'
    EAB_ARGUMENTS+=("--eab-kid" "${EAB_KID}" "--eab-hmac-key" "${EAB_HMAC_KEY}")
fi

# Generate a new certificate if necessary or expand a previous certificate if domains has changed
certbot certonly --non-interactive --keep-until-expiring --expand \
    --email "$EMAIL" --agree-tos \
    "${KEY_ARGUMENTS[@]}" \
    "${ADDITIONAL_ARGS[@]}" \
    --cert-name "${DOMAIN_ARR[1]}" "${DOMAIN_ARR[@]}" \
    --config-dir "$CERT_DIR" --work-dir "$WORK_DIR" \
    --preferred-challenges "$CHALLENGE" \
    "${ACME_ARGUMENTS[@]}" \
    "${EAB_ARGUMENTS[@]}"

# Get the last modified cert directory and copy the cert and private key to store
# shellcheck disable=SC2012
CERT_DIR_LATEST="$(ls -td $CERT_DIR/live/*/ | head -1)"
cp "${CERT_DIR_LATEST}privkey.pem" "/ssl/$KEYFILE"
cp "${CERT_DIR_LATEST}fullchain.pem" "/ssl/$CERTFILE"
